﻿using System.Linq.Expressions;
using SqlSugar;
using PmSoft.Core;
using PmSoft.Core.Domain.Entities;
using PmSoft.Core.EventBus;
using PmSoft.Data.Abstractions;
using PmSoft.Data.Abstractions.Events;
using System.Diagnostics;
using System.Reflection;
using PmSoft.Data.Abstractions.Attributes;

namespace PmSoft.Data.SqlSugar.Repositories;

/// <summary>
/// 基于 SqlSugar 的实体仓储基类，提供同步和异步的 CRUD 及查询操作
/// </summary>
/// <typeparam name="TEntity">实体类型，必须实现 IEntity<TKey></typeparam>
/// <typeparam name="TKey">主键类型，不可为空</typeparam>
public abstract class Repository<TEntity, TKey>
	where TEntity : class, IEntity<TKey>, new()
	where TKey : notnull
{
	private readonly SqlSugarDbContext _dbContext;

	/// <summary>
	/// 构造函数，初始化仓储上下文
	/// </summary>
	/// <param name="dbContext">SqlSugar 数据库上下文</param>
	/// <param name="applicationContext">应用程序上下文</param>
	protected Repository(SqlSugarDbContext dbContext, IApplicationContext applicationContext)
	{
		_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
		ApplicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext));
	}

	/// <summary>
	/// 应用程序上下文
	/// </summary>
	protected IApplicationContext ApplicationContext { get; }

	/// <summary>
	/// 事件总线，用于发布实体操作事件
	/// </summary>
	protected IEventBus EventBus => ApplicationContext.GetRequiredService<IEventBus>();

	#region Query

	/// <summary>
	/// 创建实体查询对象
	/// </summary>
	/// <returns>可查询的实体集合</returns>
	public virtual ISugarQueryable<TEntity> Query()
	{
		return _dbContext.Provider.Queryable<TEntity>();
	}

	/// <summary>
	/// 创建带条件的实体查询对象
	/// </summary>
	/// <param name="predicate">查询条件</param>
	/// <returns>可查询的实体集合</returns>
	public virtual ISugarQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate)
	{
		return Query().Where(predicate);
	}

	#endregion

	#region Exists

	/// <summary>
	/// 检查实体是否存在（同步）
	/// </summary>
	/// <param name="predicate">查询条件</param>
	/// <returns>是否存在</returns>
	public virtual bool Exists(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return Query(predicate).Any();
	}

	/// <summary>
	/// 检查实体是否存在（异步）
	/// </summary>
	/// <param name="predicate">查询条件</param>
	/// <returns>是否存在</returns>
	public virtual async Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return await Query(predicate).AnyAsync();
	}

	#endregion

	#region Insert

	/// <summary>
	/// 插入单个实体（同步）
	/// </summary>
	/// <param name="entity">要插入的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual int Insert(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		var insertedEntity = _dbContext.Provider.Insertable(entity).ExecuteReturnEntity();
		if (!Equals(insertedEntity.Id, default(TKey)))
		{
			entity.Id = insertedEntity.Id; // 更新传入实体的 ID
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
				EventBus.Publish(new EntityAddedEventArgs(ApplicationContext, entity, auditableAttribute));
			return 1;
		}
		return 0;
	}

	/// <summary>
	/// 插入多个实体（同步）
	/// </summary>
	/// <param name="entities">要插入的实体数组</param>
	/// <returns>受影响的行数</returns>
	public virtual int Insert(TEntity[] entities)
	{
		if (entities == null || entities.Length == 0) throw new ArgumentNullException(nameof(entities));
		var pks = _dbContext.Provider.Insertable(entities).ExecuteReturnPkList<TKey>();
		for (int i = 0; i < entities.Length; i++)
		{
			entities[i].Id = pks[i];
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
				EventBus.Publish(new EntityAddedEventArgs(ApplicationContext, entities[i], auditableAttribute));
		}
		return pks.Count;
	}

	/// <summary>
	/// 插入单个实体（异步）
	/// </summary>
	/// <param name="entity">要插入的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> InsertAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		var insertedEntity = await _dbContext.Provider.Insertable(entity).ExecuteReturnEntityAsync();
		if (!Equals(insertedEntity.Id, default(TKey)))
		{
			entity.Id = insertedEntity.Id; // 更新传入实体的 ID
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
				await EventBus.PublishAsync(new EntityAddedEventArgs(ApplicationContext, entity, auditableAttribute));
			return 1;
		}
		return 0;
	}

	/// <summary>
	/// 插入多个实体（异步）
	/// </summary>
	/// <param name="entities">要插入的实体数组</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> InsertAsync(TEntity[] entities)
	{
		if (entities == null || entities.Length == 0) throw new ArgumentNullException(nameof(entities));
		var pks = await _dbContext.Provider.Insertable(entities).ExecuteReturnPkListAsync<TKey>();
		for (int i = 0; i < entities.Length; i++)
		{
			entities[i].Id = pks[i];
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
				await EventBus.PublishAsync(new EntityAddedEventArgs(ApplicationContext, entities[i], auditableAttribute));
		}
		return pks.Count;
	}

	#endregion

	#region Delete

	/// <summary>
	/// 根据 ID 删除实体（同步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>受影响的行数</returns>
	public virtual int Delete(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		var entity = Get(entityId) ?? throw new ArgumentException($"未找到 ID 为 {entityId} 的实体", nameof(entityId));
		return Delete(entity);
	}

	/// <summary>
	/// 删除单个实体（同步）
	/// </summary>
	/// <param name="entity">要删除的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual int Delete(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		int rowsAffected = _dbContext.Provider.Deleteable(entity).ExecuteCommand();
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (rowsAffected > 0 && auditableAttribute != null)
			EventBus.Publish(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
		return rowsAffected;
	}

	/// <summary>
	/// 根据条件删除实体（同步）
	/// </summary>
	/// <param name="predicate">删除条件</param>
	/// <returns>受影响的行数</returns>
	public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return _dbContext.Provider.Deleteable<TEntity>().Where(predicate).ExecuteCommand();
	}

	/// <summary>
	/// 根据 ID 删除实体（异步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> DeleteAsync(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		var entity = await GetAsync(entityId) ?? throw new ArgumentException($"未找到 ID 为 {entityId} 的实体", nameof(entityId));
		return await DeleteAsync(entity);
	}

	/// <summary>
	/// 删除单个实体（异步）
	/// </summary>
	/// <param name="entity">要删除的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> DeleteAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		int rowsAffected = await _dbContext.Provider.Deleteable(entity).ExecuteCommandAsync();
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (rowsAffected > 0 && auditableAttribute != null)
			await EventBus.PublishAsync(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
		return rowsAffected;
	}

	/// <summary>
	/// 根据条件删除实体（异步）
	/// </summary>
	/// <param name="predicate">删除条件</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return await _dbContext.Provider.Deleteable<TEntity>().Where(predicate).ExecuteCommandAsync();
	}

	#endregion

	#region Update

	/// <summary>
	/// 更新单个实体（同步）
	/// </summary>
	/// <param name="entity">要更新的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual int Update(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		var oldEntity = Query().First(x => x.Id.Equals(entity.Id)) ?? throw new ArgumentException($"未找到 ID 为 {entity.Id} 的实体", nameof(entity));
		int rowsAffected = _dbContext.Provider.Updateable(entity).ExecuteCommand();
		if (rowsAffected > 0)
		{
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
			{
				if (entity is IDelEntity<TKey> delEntity && delEntity.IsDeleted
					&& oldEntity is IDelEntity<TKey> delOldEntity && !delOldEntity.IsDeleted)
					EventBus.Publish(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
				else
					EventBus.Publish(new EntityUpdatedEventArgs(ApplicationContext, oldEntity, entity, auditableAttribute));
			}
		}
		return rowsAffected;
	}

	/// <summary>
	/// 更新多个实体（同步）
	/// </summary>
	/// <param name="entities">要更新的实体集合</param>
	/// <returns>受影响的行数</returns>
	public virtual int Update(IEnumerable<TEntity> entities)
	{
		if (entities == null || !entities.Any()) throw new ArgumentNullException(nameof(entities));
		return _dbContext.Provider.Updateable(entities.ToArray()).ExecuteCommand();
	}

	/// <summary>
	/// 更新单个实体（异步）
	/// </summary>
	/// <param name="entity">要更新的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> UpdateAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		var oldEntity = await Query().FirstAsync(x => x.Id.Equals(entity.Id)) ?? throw new ArgumentException($"未找到 ID 为 {entity.Id} 的实体", nameof(entity));
		int rowsAffected = await _dbContext.Provider.Updateable(entity).ExecuteCommandAsync();
		if (rowsAffected > 0)
		{
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
			{
				if (entity is IDelEntity<TKey> delEntity && delEntity.IsDeleted
				&& oldEntity is IDelEntity<TKey> delOldEntity && !delOldEntity.IsDeleted)
					await EventBus.PublishAsync(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
				else
					await EventBus.PublishAsync(new EntityUpdatedEventArgs(ApplicationContext, oldEntity, entity, auditableAttribute));
			}
		}
		return rowsAffected;
	}

	/// <summary>
	/// 更新多个实体（异步）
	/// </summary>
	/// <param name="entities">要更新的实体集合</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> UpdateAsync(IEnumerable<TEntity> entities)
	{
		if (entities == null || !entities.Any()) throw new ArgumentNullException(nameof(entities));
		return await _dbContext.Provider.Updateable(entities.ToArray()).ExecuteCommandAsync();
	}

	#endregion

	#region Get First

	/// <summary>
	/// 获取单个实体（同步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual TEntity? Get(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return Query().First(x => x.Id.Equals(entityId));
	}

	/// <summary>
	/// 根据条件获取单个实体（同步）
	/// </summary>
	/// <param name="predicate">查询条件</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual TEntity? Get(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return Query().First(predicate);
	}

	/// <summary>
	/// 获取单个实体（异步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual async Task<TEntity?> GetAsync(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return await Query().FirstAsync(x => x.Id.Equals(entityId));
	}

	/// <summary>
	/// 根据条件获取单个实体（异步）
	/// </summary>
	/// <param name="predicate">查询条件</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual async Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> predicate)
	{
		if (predicate == null) throw new ArgumentNullException(nameof(predicate));
		return await Query().FirstAsync(predicate);
	}

	#endregion

	#region Paging

	/// <summary>
	/// 分页查询（同步）
	/// </summary>
	/// <typeparam name="TResult">结果类型</typeparam>
	/// <param name="queryFunc">查询构造函数</param>
	/// <param name="selectors">选择器</param>
	/// <param name="pageIndex">页码，从 1 开始</param>
	/// <param name="pageSize">每页大小</param>
	/// <returns>分页结果</returns>
	protected IPagedList<TResult> Paging<TResult>(
		Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> queryFunc,
		Expression<Func<TEntity, TResult>> selectors,
		int pageIndex,
		int pageSize)
	{
		if (queryFunc == null) throw new ArgumentNullException(nameof(queryFunc));
		if (selectors == null) throw new ArgumentNullException(nameof(selectors));
		if (pageIndex < 1) throw new ArgumentOutOfRangeException(nameof(pageIndex), "页码必须大于等于 1");
		if (pageSize < 1) throw new ArgumentOutOfRangeException(nameof(pageSize), "每页大小必须大于等于 1");

		var query = Query();
		int total = 0;
		var stopwatch = Stopwatch.StartNew();
		var list = queryFunc(query).Select(selectors).ToPageList(pageIndex, pageSize, ref total);
		stopwatch.Stop();

		return new PagedList<TResult>(list)
		{
			PageIndex = pageIndex,
			PageSize = pageSize,
			Total = total,
			QueryDuration = stopwatch.ElapsedMilliseconds
		};
	}

	/// <summary>
	/// 分页查询实体 ID（同步）
	/// </summary>
	/// <param name="queryFunc">查询构造函数</param>
	/// <param name="pageIndex">页码，默认 1</param>
	/// <param name="pageSize">每页大小，默认 20</param>
	/// <returns>分页的实体 ID 集合</returns>
	protected IPagedList<TKey> PagingEntityIds(
		Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> queryFunc,
		int pageIndex = 1,
		int pageSize = 20)
	{
		if (queryFunc == null) throw new ArgumentNullException(nameof(queryFunc));
		if (pageIndex < 1) throw new ArgumentOutOfRangeException(nameof(pageIndex), "页码必须大于等于 1");
		if (pageSize < 1) throw new ArgumentOutOfRangeException(nameof(pageSize), "每页大小必须大于等于 1");

		var query = Query();
		int total = 0;
		var stopwatch = Stopwatch.StartNew();
		var ids = queryFunc(query).Select(m => m.Id).ToPageList(pageIndex, pageSize, ref total);
		stopwatch.Stop();

		return new PagedList<TKey>(ids)
		{
			PageIndex = pageIndex,
			PageSize = pageSize,
			Total = total,
			QueryDuration = stopwatch.ElapsedMilliseconds
		};
	}

	/// <summary>
	/// 分页查询实体 ID（异步）
	/// </summary>
	/// <param name="queryFunc">查询构造函数</param>
	/// <param name="pageIndex">页码，默认 1</param>
	/// <param name="pageSize">每页大小，默认 20</param>
	/// <returns>分页的实体 ID 集合</returns>
	protected async Task<IPagedList<TKey>> PagingEntityIdsAsync(
		Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> queryFunc,
		int pageIndex = 1,
		int pageSize = 20)
	{
		if (queryFunc == null) throw new ArgumentNullException(nameof(queryFunc));
		if (pageIndex < 1) throw new ArgumentOutOfRangeException(nameof(pageIndex), "页码必须大于等于 1");
		if (pageSize < 1) throw new ArgumentOutOfRangeException(nameof(pageSize), "每页大小必须大于等于 1");

		var query = Query();
		RefAsync<int> total = 0;
		var stopwatch = Stopwatch.StartNew();
		var ids = await queryFunc(query).Select(m => m.Id).ToPageListAsync(pageIndex, pageSize, total);
		stopwatch.Stop();

		return new PagedList<TKey>(ids)
		{
			PageIndex = pageIndex,
			PageSize = pageSize,
			Total = total.Value,
			QueryDuration = stopwatch.ElapsedMilliseconds
		};
	}

	#endregion
}